#include "esp_log.h"
#include "esp_system.h"
#include "esp_err.h"
#include "driver/i2s_std.h"
#include "esp_vfs_fat.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define TAG "music player"

#define BUFFER_SIZE 64 

// WAV header
typedef struct
{
    char riff_header[4];      // Contains "RIFF"
    uint32_t wav_size;        // Size of WAV f in bytes - 8
    char wave_header[4];      // Contains "WAVE"
    char fmt_header[4];       // Contains "fmt "
    uint32_t fmt_chunk_size;  // Size of fmt chunk in bytes
    uint16_t audio_format;    // Audio format - PCM = 1
    uint16_t num_channels;    // Number of audio channels - Mono = 1, Stereo = 2, etc.
    uint32_t sample_rate;     // Sampling rate in Hz
    uint32_t byte_rate;       // Average bytes per second
    uint16_t block_align;     // Block alignment (number of bytes per sample)
    uint16_t bits_per_sample; // Number of bits per sample
    char data_header[4];      // Contains "data"+++
    uint32_t data_bytes;      // Number of bytes in data
} WAVHeader_t;

static float volume = 0.05;
static char *f_path;

static void wav_player(void *arg);
static void print_wav_header(WAVHeader_t *header);
void set_volume(float volume_value);

static void print_wav_header(WAVHeader_t *header)
{
    printf("WAV header:\n");
    printf("  RIFF header:     %c%c%c%c\n", header->riff_header[0], header->riff_header[1], header->riff_header[2], header->riff_header[3]);
    printf("  WAV size:        %ld\n", header->wav_size);
    printf("  Wave header:     %c%c%c%c\n", header->wave_header[0], header->wave_header[1], header->wave_header[2], header->wave_header[3]);
    printf("  FMT header:      %c%c%c%c\n", header->fmt_header[0], header->fmt_header[1], header->fmt_header[2], header->fmt_header[3]);
    printf("  FMT chunk size:  %ld\n", header->fmt_chunk_size);
    printf("  Audio format:    %d\n", header->audio_format);
    printf("  Num channels:    %d\n", header->num_channels);
    printf("  Sample rate:     %ld\n", header->sample_rate);
    printf("  Byte rate:       %ld\n", header->byte_rate);
    printf("  Block align:     %d\n", header->block_align);
    printf("  Bits per sample: %d\n", header->bits_per_sample);
    printf("  Data header:     %c%c%c%c\n", header->data_header[0], header->data_header[1], header->data_header[2], header->data_header[3]);
    printf("  Data bytes:      %ld\n", header->data_bytes);
}

void set_volume(float volume_value)
{
    if (volume_value <= 100 && volume_value >= 1)
    {
        vTaskSuspendAll();
        volume = volume_value * 0.001;
        xTaskResumeAll();
    }
    if (volume_value > 100 && volume_value < 1)
    {
        ESP_LOGE(TAG, "Exceeding the adjustment range");
    }
    if (volume_value == 0)
    {
        vTaskSuspendAll();
        volume = 0;
        xTaskResumeAll();
    }
}

static void wav_player(void *arg)
{
    // Open WAV f
    char *f_path = "/sdcard/xlzy.wav";
    FILE *file = fopen(f_path, "r");
    if (file == NULL)
    {
        ESP_LOGE(TAG, "Failed to open f \"%s\"", f_path);
        return;
    }

    // Read WAV header
    WAVHeader_t header;
    size_t read_count = fread(&header, sizeof(WAVHeader_t), 1, file);
    if (read_count != 1)
    {
        ESP_LOGE(TAG, "Failed to read WAV header");
        return;
    }
    print_wav_header(&header);

    i2s_chan_handle_t tx_handle;
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
    i2s_new_channel(&chan_cfg, &tx_handle, NULL);

    i2s_std_config_t std_cfg = {
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(header.sample_rate),
        .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(header.bits_per_sample, I2S_SLOT_MODE_STEREO),
        .gpio_cfg = {
            .mclk = -1,
            .bclk = 34,
            .ws = 33,
            .dout = 35,
            .din = -1,
            .invert_flags = {
                .mclk_inv = false,
                .bclk_inv = false,
                .ws_inv = false,
            },
        },
    };
    /* Initialize the channel */
    i2s_channel_init_std_mode(tx_handle, &std_cfg);
    /* Before write data, start the tx channel first */
    i2s_channel_enable(tx_handle);

    int16_t buffer[BUFFER_SIZE];

    fseek(file, 44, 0);
    while (true)
    {
        size_t bytes_read = fread(buffer, 1, BUFFER_SIZE, file);
        if (bytes_read == 0)
        {
            ESP_LOGI(TAG, "End of file reached");
            break;
        }

        for (int i = 0; i < BUFFER_SIZE; i++)
        {
            vTaskSuspendAll();
            buffer[i] = buffer[i] * volume;
            xTaskResumeAll();
        }

        size_t bytes_written = 0;
        if (i2s_channel_write(tx_handle, buffer, BUFFER_SIZE, &bytes_written, 1000 / portTICK_PERIOD_MS) != ESP_OK)
        {
            ESP_LOGE(TAG, "Failed to send data to I2S");
            break;
        }
    }

    fclose(file);
    /* Have to stop the channel before deleting it */
    i2s_channel_disable(tx_handle);
    /* If the handle is not needed any more, delete it to release the channel resources */
    i2s_del_channel(tx_handle);

    ESP_LOGI(TAG, "Finished playing file");
    vTaskDelete(NULL);
}

void music_player(char *path)
{
    f_path = path;
    xTaskCreate(wav_player, "music_play_task", 1024 * 4, NULL, 5, NULL);
}